package com.sun.asyncimporttask.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.sun.asyncimporttask.dto.ImportErrorRecord;
import com.sun.asyncimporttask.dto.TaskInfo;
import com.sun.asyncimporttask.dto.UserImportDTO;
import com.sun.asyncimporttask.entity.ImportTask;
import com.sun.asyncimporttask.enums.ImportStatus;
import com.sun.asyncimporttask.exception.ResourceNotFoundException;
import com.sun.asyncimporttask.listner.UserImportListener;
import com.sun.asyncimporttask.mapper.ImportTaskMapper;
import com.sun.asyncimporttask.mapper.UserMapper;
import com.sun.asyncimporttask.service.ImportService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 导入服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ImportServiceImpl implements ImportService {
    private final ImportTaskMapper taskMapper;
    private final UserMapper userMapper;

    @Value("${import.file.dir:/tmp/excel_imports}")
    private String importFileDir;

    // 线程安全的错误记录存储
    private final Map<String, List<ImportErrorRecord>> errorRecords = new ConcurrentHashMap<>();

    @Override
    public String asyncImport(MultipartFile file) {
        String taskId = UUID.randomUUID().toString();
        String filePath = saveUploadFile(file, taskId);

        ImportTask task = new ImportTask()
                .setTaskId(taskId)
                .setOriginalFilename(file.getOriginalFilename())
                .setFilePath(filePath)
                .setStatus(ImportStatus.PENDING)
                .setCreateTime(LocalDateTime.now())
                .setTotalCount(getTotalRows(filePath));

        taskMapper.insert(task);

        // 异步执行导入
        new Thread(() -> processImport(taskId)).start();

        return taskId;
    }

    private void processImport(String taskId) {
        ImportTask task = taskMapper.selectById(taskId);
        if (task == null) {
            log.error("任务不存在: {}", taskId);
            return;
        }

        // 初始化计数器（新增）
        task.setSuccessCount(0).setFailCount(0);
        taskMapper.updateById(task);
        updateTaskStatus(taskId, ImportStatus.PROCESSING);

        try {
            // 初始化错误记录
            errorRecords.put(taskId, new ArrayList<>());

            // 读取并处理Excel
            ExcelReader excelReader = EasyExcel.read(
                    new File(task.getFilePath()),
                    UserImportDTO.class,
                    new UserImportListener(taskId, this, userMapper)
            ).build();

            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            excelReader.read(readSheet);
            excelReader.finish();

            // 处理结果
            List<ImportErrorRecord> errors = errorRecords.getOrDefault(taskId, Collections.emptyList());
            if (!errors.isEmpty()) {
                generateErrorFile(task, errors);
                updateTaskStatus(taskId,
                        errors.size() >= task.getTotalCount() ?
                                ImportStatus.FAILED : ImportStatus.PARTIAL_SUCCESS);
            } else {
                updateTaskStatus(taskId, ImportStatus.COMPLETED);
            }
        } catch (Exception e) {
            log.error("导入处理异常", e);
            updateTaskStatus(taskId, ImportStatus.FAILED);
        } finally {
            errorRecords.remove(taskId);
        }
    }

    @Override
    public void addErrorRecord(String taskId, ImportErrorRecord record) {
        errorRecords.computeIfAbsent(taskId, k -> new ArrayList<>()).add(record);
    }

    @Override
    public void incrementSuccess(String taskId) {
        taskMapper.incrementSuccess(taskId);
    }

    @Override
    public void incrementFail(String taskId) {
        taskMapper.incrementFail(taskId);
    }

    @Override
    public TaskInfo getTaskInfo(String taskId) {
        ImportTask task = taskMapper.selectById(taskId);
        if (task == null) {
            throw new ResourceNotFoundException("任务不存在");
        }

        return convertToTaskInfo(task);
    }

    @Override
    public List<TaskInfo> listTasks() {
        return taskMapper.selectList(null).stream()
                .map(this::convertToTaskInfo)
                .sorted(Comparator.comparing(TaskInfo::getCreateTime).reversed())
                .collect(Collectors.toList());
    }

    @Override
    public Resource getErrorFileResource(String taskId) {
        ImportTask task = taskMapper.selectById(taskId);
        if (task == null || task.getErrorFilePath() == null) {
            throw new ResourceNotFoundException("错误文件不存在");
        }

        return new FileSystemResource(Paths.get(task.getErrorFilePath()));
    }

    @Override
    public int getTotalRows(String filePath) {
        try {
            return EasyExcel.read(new File(filePath))
                    .sheet()
                    .doReadSync()
                    .size() - 1; // 减去表头
        } catch (Exception e) {
            log.error("获取总行数失败", e);
            return 0;
        }
    }

    private String saveUploadFile(MultipartFile file, String taskId) {
        try {
            Path dirPath = Paths.get(importFileDir);
            if (!Files.exists(dirPath)) {
                Files.createDirectories(dirPath);
            }

            String filename = taskId + "_" + file.getOriginalFilename();
            Path filePath = dirPath.resolve(filename);
            file.transferTo(filePath.toFile());

            // 保存后立即获取总行数（新增）
            int totalCount = getTotalRows(filePath.toString());
            taskMapper.updateTotalCount(taskId, totalCount);

            return filePath.toString();
        } catch (IOException e) {
            throw new RuntimeException("文件保存失败", e);
        }
    }

    private void generateErrorFile(ImportTask task, List<ImportErrorRecord> errors) {
        Path errorPath = null;
        try {
            // 1. 读取原始数据
            List<Map<Integer, String>> originalData = EasyExcel.read(new File(task.getFilePath()))
                    .sheet()
                    .doReadSync();

            // 2. 准备错误数据
            List<List<String>> dataWithErrors = new ArrayList<>();

            // 3. 处理表头（创建新集合而非修改原集合）
//            Map<Integer, String> headerRow = originalData.get(0);
//            List<String> newHeader = new ArrayList<>(headerRow.size() + 1);
//            newHeader.addAll(headerRow.values()); // 添加原始列
//            newHeader.add("错误信息"); // 追加错误列
//            dataWithErrors.add(newHeader);

            // 4. 处理数据行
            for (int i = 0; i < originalData.size(); i++) {
                Map<Integer, String> originalRow = originalData.get(i);
                int excelRowNum = i + 2; // Excel实际行号

                // 4.1 创建新行数据（ArrayList可修改）
                List<String> newRow = new ArrayList<>(originalRow.size() + 1);
                newRow.addAll(originalRow.values());

                // 4.2 添加错误信息
                String errorMsg = errors.stream()
                        .filter(e -> e.getRowIndex() == excelRowNum)
                        .findFirst()
                        .map(ImportErrorRecord::getErrorMessage)
                        .orElse("");
                newRow.add(errorMsg);

                dataWithErrors.add(newRow);
            }

            // 5. 写入错误文件
            errorPath = Paths.get(importFileDir, "error_" + task.getOriginalFilename());
            EasyExcel.write(errorPath.toFile())
                    .head(buildHeaders(originalData.get(0).size()))
                    .sheet("错误报告")
                    .doWrite(dataWithErrors);

            // 6. 更新任务记录
            task.setErrorFilePath(errorPath.toString());
            taskMapper.updateById(task);

        } catch (Exception e) {
            log.error("生成错误文件失败", e);
            // 清理可能创建的不完整文件
            if (errorPath != null) {
                try {
                    Files.deleteIfExists(errorPath);
                } catch (IOException ex) {
                    log.warn("无法删除临时错误文件: {}", errorPath, ex);
                }
            }
        }
    }

    // 构建表头（修正版）
    private List<List<String>> buildHeaders(int originalColumnCount) {
        List<List<String>> headers = new ArrayList<>();

        // 添加原始列头（根据实际业务调整）
        for (int i = 0; i < originalColumnCount; i++) {
            headers.add(Collections.singletonList("列" + (i + 1)));
        }

        // 添加错误信息列头
        headers.add(Collections.singletonList("错误信息"));

        return headers;
    }

//    private List<List<String>> buildHeaders(int originalColumnCount) {
//        List<List<String>> headers = new ArrayList<>();
//        headers.add(Arrays.asList("用户名", "年龄", "地址"));
//        headers.add(Collections.singletonList("错误信息"));
//        return headers;
//    }

//    private void generateErrorFile(ImportTask task, List<ImportErrorRecord> errors) {
//        try {
//            // 读取原始数据
//            List<Map<Integer, String>> originalData = EasyExcel.read(task.getFilePath())
//                    .sheet()
//                    .doReadSync();
//
//            // 构建带错误列的数据
//            List<List<Object>> dataWithErrors = new ArrayList<>();
//
//            // 添加表头
//            Map<Integer, String> headerRow = originalData.get(0);
//            List<Object> newHeader = new ArrayList<>(headerRow.values());
//            newHeader.add("错误信息");
//            dataWithErrors.add(newHeader);
//
//            // 处理数据行
//            for (int i = 1; i < originalData.size(); i++) {
//                Map<Integer, String> row = originalData.get(i);
//                int excelRowNum = i + 1;
//
//                String errorMsg = errors.stream()
//                        .filter(e -> e.getRowIndex() == excelRowNum)
//                        .findFirst()
//                        .map(ImportErrorRecord::getErrorMessage)
//                        .orElse("");
//
//                dataWithErrors.add(Stream.concat(row.values().stream(), Stream.of(errorMsg)).collect(Collectors.toList()));
//            }
//
//            // 写入错误文件
//            String errorFileName = "error_" + task.getOriginalFilename();
//            Path errorPath = Paths.get(importFileDir, errorFileName);
//
//            EasyExcel.write(errorPath.toFile())
//                    .head(buildHeaders(originalData.get(0).size()))
//                    .sheet("错误报告")
//                    .doWrite(dataWithErrors);
//
//            task.setErrorFilePath(errorPath.toString());
//            taskMapper.updateById(task);
//        } catch (Exception e) {
//            log.error("生成错误文件失败", e);
//        }
//    }
//
//    private List<List<String>> buildHeaders(int originalColumnCount) {
//        List<List<String>> headers = new ArrayList<>();
//        headers.add(Arrays.asList("用户名", "年龄", "地址"));
//        headers.add(Collections.singletonList("错误信息"));
//        return headers;
//    }

    private void updateTaskStatus(String taskId, ImportStatus status) {
        taskMapper.updateStatus(taskId, status.name());

        if (status.isFinalStatus()) {
            taskMapper.updateFinishTime(taskId, LocalDateTime.now());
        }
    }

    private TaskInfo convertToTaskInfo(ImportTask task) {
        return new TaskInfo()
                .setTaskId(task.getTaskId())
                .setOriginalFilename(task.getOriginalFilename())
                .setStatus(task.getStatus())
                .setErrorFileUrl(task.getErrorFilePath() != null ?
                        "/api/import/tasks/" + task.getTaskId() + "/error-file" : null)
                .setCreateTime(task.getCreateTime())
                .setFinishTime(task.getFinishTime())
                .setTotalCount(task.getTotalCount())
                .setSuccessCount(task.getSuccessCount())
                .setFailCount(task.getFailCount());
    }
}